package com.soc.auth.filter;

import com.soc.auth.constants.RedisConstants;
import com.soc.auth.domain.dto.RedisUser;
import com.soc.auth.enumes.ResponseStatusEnum;
import com.soc.auth.security.RedisAuthenticationToken;
import com.soc.auth.utils.JwtUtil;
import com.soc.auth.utils.ResponseUtil;
import com.soc.auth.utils.UrlPatternUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    //白名单
    private static final List<String> ignoreUrls = Arrays.asList("/api/sendSmsCode", "/user/registerStudent", "/user/login", "/file/**","/api/user/forgetPassword");

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        // 从请求头中获取jwt
        String accessToken = request.getHeader("token");

        String requestURI = request.getRequestURI();
        if (!StringUtils.hasText(accessToken) || skipValid(requestURI)) {
            // token为空或者url是则放行
            filterChain.doFilter(request,response);

            //在doFilter后return，防止响应经过该过滤器时会执行下面的代码
            return;
        }

        String userId;
        try {
            Claims claims = JwtUtil.parseJWT(accessToken);
            userId = claims.getSubject();
        } catch (Exception e) {
            log.error("accessToken 过期或者格式无效");
            ResponseUtil.response(response, ResponseStatusEnum.AccessTokenError);
            // 使请求立即返回
            return;
        }

        // 从 Redis 中获取
        RedisUser redisUser = (RedisUser) redisTemplate.opsForValue().get(RedisConstants.LOGIN_USER_KEY + userId);

        if (Objects.isNull(redisUser)) {
            // 用户未登录
            ResponseUtil.response(response, ResponseStatusEnum.UNAUTHORIZED);
            return;
        }

        List<GrantedAuthority> grantedAuthorities = redisUser.getRoles().stream()
                .map((Function<String, GrantedAuthority>) SimpleGrantedAuthority::new).collect(Collectors.toList());
        RedisAuthenticationToken redisAuthenticationToken = new RedisAuthenticationToken(redisUser.getUser(), "", grantedAuthorities);

        //将authenticationToken存入SecurityContextHolder
        SecurityContextHolder.getContext().setAuthentication(redisAuthenticationToken);

        //放行
        filterChain.doFilter(request, response);
    }

    /**
     * 跳过校验
     */
    private boolean skipValid(String path) {
        for (String skipPath : ignoreUrls) {
            if (UrlPatternUtils.match(skipPath, path)) {
                return true;
            }
        }
        return false;
    }
}
